import { ref, watch, onUnmounted, type Ref } from 'vue'

// 类型守卫函数
function isRef<T>(val: any): val is Ref<T> {
  return val && typeof val === 'object' && val.__v_isRef === true
}

export function useDebounce<T>(value: Ref<T> | T, delay: number = 300) {
  const debouncedValue = ref<T>(isRef(value) ? value.value : value)
  let timeoutId: number | null = null

  const updateDebouncedValue = (newValue: T) => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId)
    }

    timeoutId = setTimeout(() => {
      debouncedValue.value = newValue
      timeoutId = null
    }, delay)
  }

  // 清理定时器
  const cleanup = () => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId)
      timeoutId = null
    }
  }

  // 如果传入的是 ref，监听其变化
  if (isRef(value)) {
    watch(value, updateDebouncedValue, { immediate: false })
  }

  // 手动触发更新的方法
  const trigger = (newValue?: T) => {
    if (newValue !== undefined) {
      updateDebouncedValue(newValue)
    } else if (isRef(value)) {
      updateDebouncedValue(value.value)
    }
  }

  // 立即更新（跳过防抖）
  const flush = () => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId)
      if (isRef(value)) {
        debouncedValue.value = value.value
      }
      timeoutId = null
    }
  }

  // 取消防抖
  const cancel = () => {
    cleanup()
  }

  // 组件卸载时清理定时器
  onUnmounted(cleanup)

  return {
    debouncedValue,
    trigger,
    flush,
    cancel,
  }
}

// 防抖函数版本
export function useDebounceFn<T extends (...args: any[]) => any>(fn: T, delay: number = 300) {
  let timeoutId: number | null = null

  const cleanup = () => {
    if (timeoutId !== null) {
      clearTimeout(timeoutId)
      timeoutId = null
    }
  }

  const debouncedFn = (...args: Parameters<T>) => {
    cleanup()

    timeoutId = setTimeout(() => {
      fn(...args)
      timeoutId = null
    }, delay)
  }

  const cancel = () => {
    cleanup()
  }

  const flush = (...args: Parameters<T>) => {
    cancel()
    return fn(...args)
  }

  // 组件卸载时清理定时器
  onUnmounted(cleanup)

  return {
    debouncedFn,
    cancel,
    flush,
  }
}
